package com.foundationmade.newbiz.business.businesscard.service;

import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.foundationmade.newbiz.business.businesscard.domain.BusinessCard;

import devcken.hibernate.query.OrderBy;
import devcken.reflect.ReflectionUtils;
import devcken.utility.Pair;

/**
 * 명함에 대한 데이터베이스 액세스를 제공한다.
 * @author Devcken
 */
@Service("businessCardService")
@Transactional
public class BusinessCardService
{
	/**
	 * NewBiz {@link org.hibernate.SessionFactory}를 주입받는다.
	 */
	@Resource(name="newbizSessionFactory")
	private SessionFactory _sessionFactory;
	
	/**
	 * 주어진 검색 조건으로 명함 목록을 조회한다.
	 * @param searchCondition 명함 목록을 조회하기 위한 검색 조건을 설정한다.
	 * @param page 표시할 페이지를 설정한다.
	 * @param pageSize 한 페이지 당 표시할 레코드 수를 설정한다.
	 * @param sortConditions 정렬 조건을 설정한다.
	 * @param businessCards 조회된 명함 목록을 참조한다.
	 * @return 검색 조건으로 조회된 전체 명함의 개수가 반환된다.
	 */
	@SuppressWarnings("unchecked")
	public Integer listBusinessCards(BusinessCard searchCondition, int page, int pageSize, List<Pair<String, OrderBy>> sortConditions, List<BusinessCard> businessCards)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria criteria = session.createCriteria(BusinessCard.class);
			
			if (StringUtils.isNotEmpty(searchCondition.getName()))
			{
				criteria.add(Restrictions.like("name", String.format("%%%s%%", searchCondition.getName())));
			}
			
			if (StringUtils.isNotEmpty(searchCondition.getPhoneNumber()))
			{
				criteria.add(Restrictions.like("phoneNumber", String.format("%%%s%%", searchCondition.getPhoneNumber())));
			}
			
			if (StringUtils.isNotEmpty(searchCondition.getCompany()))
			{
				criteria.add(Restrictions.like("company", String.format("%%%s%%", searchCondition.getCompany())));
			}
			
			if (StringUtils.isNotEmpty(searchCondition.getPosition()))
			{
				criteria.add(Restrictions.like("position", String.format("%%%s%%", searchCondition.getPosition())));
			}
			
			if (StringUtils.isNotEmpty(searchCondition.getDescription()))
			{
				criteria.add(Restrictions.like("description", String.format("%%%s%%", searchCondition.getDescription())));
			}
			
			if (sortConditions.size() == 0)
			{
				criteria.addOrder(Order.desc("generatedTimestamp"));
			}
			
			for (int index = 0; index < sortConditions.size(); index++)
			{
				Pair<String, OrderBy> sortCondition = sortConditions.get(index);
				
				switch (sortCondition.getRight())
				{
					case Ascending:
						
						criteria.addOrder(Order.asc(sortCondition.getLeft()));
						
						break;
						
					case Descending:
						
						criteria.addOrder(Order.desc(sortCondition.getLeft()));
						
						break;
				}
			}
			
			Integer filteredCount = ((Long)criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
			
			criteria.setProjection(null);
			criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
			
			criteria.setFirstResult(pageSize * (page - 1)).setMaxResults(pageSize);
			
			businessCards.addAll(criteria.list());
			
			return filteredCount;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 명함 정보 개수를 가져온다.
	 * @return 명함 정보 개수가 반환된다.
	 */
	public Long countBusinessCards()
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			return (Long)session.createCriteria(BusinessCard.class).setProjection(Projections.rowCount()).uniqueResult();
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 새로운 명함 정보를 저장한다.
	 * @param businessCard 저장하려는 명함 정보를 설정한다.
	 * @return 명함 정보가 저장됐으면 true, 그렇지 않으면 false가 반환된다.
	 */
	public boolean saveBusinessCard(BusinessCard businessCard)
	{
		Session session = this._sessionFactory.openSession();
		
		Transaction transaction = session.beginTransaction();
		
		try
		{
			if (businessCard.getCardId() > 0)
			{
				session.update(businessCard);
			}
			else
			{
				session.save(businessCard);	
			}
			
			transaction.commit();
			
			return true;
		}
		catch (Exception error)
		{
			transaction.rollback();
			
			throw error;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}

	/**
	 * 수정된 명함 정보를 저장한다.
	 * @param cardId 저장하려는 명함의 아이디를 설정한다.
	 * @param propertyName 저장하려는 명함 정보의 프로퍼티 명을 설정한다.
	 * @param propertyValue 저장하려는 명함 정보의 프로퍼티 값을 설정한다.
	 * @return 명함 정보가 저장됐으면 true, 그렇지 않으면 false가 반환된다.
	 * @throws Exception
	 */
	public boolean updateBusinessCard(Long cardId, String propertyName, String propertyValue) throws Exception
	{
		if (cardId < 1)
		{
			return false;
		}
		
		Session session = this._sessionFactory.openSession();
		
		Transaction transaction = session.beginTransaction();
		
		try
		{
			BusinessCard card = (BusinessCard)session.get(BusinessCard.class, cardId);
			
			ReflectionUtils.invokeSetter(card, propertyName, String.class, propertyValue);
			
			card.setUpdatedTimestamp(new Date());
			
			session.update(card);
			
			transaction.commit();
			
			return true;
		}
		catch (NoSuchMethodException error)
		{
			transaction.rollback();
			
			throw error;
		}
		catch (Exception error)
		{
			transaction.rollback();
			
			throw error;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
}